﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CSharpExperiments.Collections
{
	public class Bag<T>
	{
		public Bag()
		{
			dict = new Dictionary<T, int>();
		}

		public Bag(IEqualityComparer<T> comparer)
		{
			dict = new Dictionary<T, int>(comparer);
		}

		public Bag(IDictionary<T, int> dict)
		{
			this.dict = dict;
		}

		private IDictionary<T, int> dict;

		public int Add(T item)
		{
			return Add(item, 1);
		}

		public int Add(T item, int n)
		{
			if (dict.ContainsKey(item))
			{
				int newCount = Math.Max(0, dict[item] + n);

				if (newCount == 0)
				{
					dict.Remove(item);
				}
				else
				{
					dict[item] = newCount;
				}

				return newCount;
			}
			else if (n > 0)
			{
				dict.Add(item, n);
				return n;
			}
			
			return 0;
		}

		public int Remove(T item)
		{
			return Remove(item, 1);
		}

		public int Remove(T item, int n)
		{
			return Add(item, -n);
		}

		public void RemoveAll(T item)
		{
			dict.Remove(item);
		}

		public void Clear()
		{
			dict.Clear();
		}

		public int Count(T item)
		{
			return dict.ContainsKey(item) ? dict[item] : 0;
		}

		public int ItemCount
		{
			get { return dict.Count; }
		}

		public int TotalCount
		{
			get { return dict.Values.Sum(); }
		}
	}

	public static class BagLinq
	{
		public static Bag<T> ToBag<T>(this IEnumerable<T> e)
		{
			var bag = new Bag<T>();

			foreach (T t in e)
				bag.Add(t);

			return bag;
		}

		public static Bag<T> ToBag<T>(this IEnumerable<T> e, IEqualityComparer<T> comparer)
		{
			var bag = new Bag<T>(comparer);

			foreach (T t in e)
				bag.Add(t);

			return bag;
		}

		public static Bag<T> AsBag<T>(this IDictionary<T, int> d)
		{
			return new Bag<T>(d);
		}
	}
}
